package com.zs.spring;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: ZhengShan
 * @CreateTime: 2024-06-17
 * @Description: ApplicationContext
 * @Version 1.0
 */
public class TestApplicationContext {

    private Class appConfig;

    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    private ConcurrentHashMap<String, Object> singletonObjectMap = new ConcurrentHashMap<>();

    private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    private final String SINGLETON = "singleton";

    public TestApplicationContext(Class appConfig){
        this.appConfig = appConfig;
        //扫描 判断@ComponentScan注解是否在appConfig上
        //扫描 -> BeanDefinition -> beanDefinitionMap
        if(appConfig.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScan = (ComponentScan) appConfig.getAnnotation(ComponentScan.class);
            //获取扫描路径 例如：com.zs.service
            String path = componentScan.value().replace(".", "/");
            ClassLoader classLoader = TestApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);
            File file = new File(resource.getFile());
            System.out.println("file" + file);
            if(file.isDirectory()) {
                File[] files = file.listFiles();
                for(File f : files) {
                    String fileName = f.getAbsolutePath();
                    System.out.println("fileName:" + fileName);
                    if(fileName.endsWith(".class")) {
                        String classname = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                         classname = classname.replace("\\", ".");
                         System.out.println("classname:" + classname);
                        //通过反射判断这个类有没有@ComponentScan注解
                        //类的加载
                        Class<?> clazz = null;
                        try {
                            clazz = classLoader.loadClass(classname);
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                        if(clazz.isAnnotationPresent(Component.class)) {
                            //判断clazz是否实现了beanPostProcessor接口
                            if(BeanPostProcessor.class.isAssignableFrom(clazz)){
                                try {
                                    BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.newInstance();
                                    beanPostProcessors.add(beanPostProcessor);
                                } catch (InstantiationException e) {
                                    e.printStackTrace();
                                } catch (IllegalAccessException e) {
                                    e.printStackTrace();
                                }
                            }

                            //获取bean的名字
                            Component component = clazz.getAnnotation(Component.class);
                            //@Component注解中定义的bean的名字
                            String beanName = component.value();

                            if("".equals(beanName)) {
                                beanName = Introspector.decapitalize(clazz.getSimpleName());
                            }

                            //如果存在@Component注解，就是bean
                            //生成一个BeanDefinition对象
                            BeanDefinition beanDefinition = new BeanDefinition();
                            //判断有没有@Scope注解， （根据自己的逻辑定义，我这边是：有就获取，没有就是单例）
                            if(clazz.isAnnotationPresent(Scope.class)) {
                                Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
                                beanDefinition.setScope(scopeAnnotation.value());
                            } else {
                                beanDefinition.setScope("singleton");
                            }
                            beanDefinition.setType(clazz);
                            beanDefinitionMap.put(beanName, beanDefinition);
                        }
                    }
                }
            }
        }

        //获取全部单例bean,并实例化单例bean
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if(beanDefinition.getScope().equals(SINGLETON)) {
                Object bean = createBean(beanName, beanDefinition);
                singletonObjectMap.put(beanName, bean);
            }
        }
    }

    public Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getType();

        try {
            //这里默认当前类有无参构造方法
            Object instance = clazz.getConstructor().newInstance();

            //依赖注入  创建的时候同时依赖注入
            //getFields()：获得某个类的所有的公共（public）的字段，包括父类中的字段。
            //getDeclaredFields()：获得某个类的所有声明的字段，即包括public、private和proteced，但是不包括父类的申明字段
            for (Field f : clazz.getDeclaredFields()) {
                if(f.isAnnotationPresent(Autowired.class)) {
                    f.setAccessible(true);
                    f.set(instance, getBean(f.getName()));
                }
            }

            //Aware回调
            if(instance instanceof BeanNameAware) {
                ((BeanNameAware)instance).setBeanName(beanName);
            }

            //初始化前 AOP  beanPostProcessor
            for(BeanPostProcessor beanPostProcessor : beanPostProcessors){
                beanPostProcessor.postProcessBeforeInitialization(beanName, clazz);
            }

            //初始化
            if(instance instanceof InitializingBean) {
                ((InitializingBean)instance).afterPropertiesSet();
            }

            //初始化后 AOP  beanPostProcessor
            for(BeanPostProcessor beanPostProcessor : beanPostProcessors){
                instance = beanPostProcessor.postProcessAfterInitialization(beanName, clazz);
            }

            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if(beanDefinition == null) {
            throw new NullPointerException();
        }
        //存在bean  判断是单例还是多例
        String scope = beanDefinition.getScope();
        if(SINGLETON.equals(scope)) {
            Object bean = singletonObjectMap.get(beanName);
            if(bean == null) {
                bean = createBean(beanName, beanDefinition);
                singletonObjectMap.put(beanName, bean);
            }
            return bean;
        } else {
            return createBean(beanName, beanDefinition);
        }
    }
}
